package com.platform.common.secret.utils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;


import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
/**
 * @author yzg
 * @date 2023/4/18
 * @apiNote
 * PBE是一种基于口令的加密算法，使用口令代替其他对称加密算法中的密钥，其特点在于口令由用户自己掌管，不借助任何物理媒体；采用随机数（这里我们叫做盐）杂凑多重加密等方法保证数据的安全性。
 *
 * PBE算法是对称加密算法的综合算法，常见算法PBEWithMD5AndDES,使用MD5和DES算法构建了PBE算法。将盐附加在口令上，通过消息摘要算法经过迭代获得构建密钥的基本材料，构建密钥后使用对称加密算法进行加密解密。
 *
 * 算法/密钥长度/默认密钥长度：
 *
 * 1.PBEWithMD5AndDES/56/56
 *
 * 2.PBEWithMD5AndTripleDES/112,168/168
 *
 * 3.PBEWithSHA1AndDESede/112,168/168
 *
 * 4.PBEWithSHA1AndRC2_40/40 to 1024/128
 * 工作模式：CBC
 *
 * 填充方式：PKCS5Padding
 *
 *
 */
public class PBEUtil {


    public static final String ALGORITHM = "PBEWITHMD5andDES";

    public static final int ITERATION_COUNT = 100;


    public static byte[] initSalt() throws Exception{
        //实例化安全随机数
        SecureRandom random = new SecureRandom();
        return random.generateSeed(8);
    }

    /***
     * 转换密钥
     * @param password 密码
     * @return 密钥
     * @throws Exception
     */
    private static Key toKey(String password) throws Exception{
        //密钥材料
        PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
        //实例化
        SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
        //生成密钥
        return factory.generateSecret(keySpec);
    }

    /***
     * 加密
     * @param data 待加密数据
     * @param password 密钥
     * @param salt
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, String password, byte[] salt) throws Exception{
        //转换密钥
        Key key = toKey(password);
        //实例化PBE参数材料
        PBEParameterSpec spec = new PBEParameterSpec(salt, ITERATION_COUNT);
        //实例化
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        //初始化
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);
        return cipher.doFinal(data);
    }
    /***
     * 加密
     * @param data 待加密数据
     * @param password 密钥
     * @param salt
     * @return
     * @throws Exception
     */
    public static String encrypt(String data, String password, byte[] salt) throws Exception{
        //转换密钥
        Key key = toKey(password);
        //实例化PBE参数材料
        PBEParameterSpec spec = new PBEParameterSpec(salt, ITERATION_COUNT);
        //实例化
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        //初始化
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);
        byte[] encryptData =cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return new BASE64Encoder().encode(encryptData);//此处使用BASE64做转码功能，同时能起到2次加密的作用。
    }

    /***
     * 解密
     * @param data 待解密数据
     * @param password 密钥
     * @param salt
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data, String password, byte[] salt) throws Exception{
        //转换密钥
        Key key = toKey(password);
        //实例化PBE参数材料
        PBEParameterSpec spec = new PBEParameterSpec(salt, ITERATION_COUNT);
        //实例化
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        //初始化
        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        //执行操作
        return cipher.doFinal(data);
    }

    /***
     * 解密
     * @param data 待解密数据
     * @param password 密钥
     * @param salt
     * @return
     * @throws Exception
     */
    public static String decrypt(String data, String password, byte[] salt) throws Exception{
        //转换密钥
        Key key = toKey(password);
        //实例化PBE参数材料
        PBEParameterSpec spec = new PBEParameterSpec(salt, ITERATION_COUNT);
        //实例化
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        //初始化
        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        byte[] encryptData = new BASE64Decoder().decodeBuffer(data);
        //执行操作
        byte[] decryptData = cipher.doFinal(encryptData);
        return new String(decryptData, "utf-8");
    }


    private static String showByteArray(byte[] data) {
        if(null == data) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for(byte b : data) {
            sb.append(b).append(",");
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append("");
        return sb.toString();
    }
    public static void main(String[] args) throws Exception{
        byte[] salt = initSalt();

        String clientId=new String(salt, StandardCharsets.ISO_8859_1);
        String clientSecret=ToolUtil.getRandomSm4Key();
        System.out.println("salt："+showByteArray(salt));
        System.out.println("口令："+clientSecret);
        String data = "{\"approveStatus\":\"0\",\"approveBy\":\"\",\"appLogo\":\"tool\",\"secretStatus\":\"1\",\"secretCode\":\"PBE\",\"secretId\":13,\"appCode\":\"Test\",\"principalTel\":\"1ce2a3a0fc725418eb97e7cfbf857764\",\"principal\":\"1\",\"updateBy\":\"\",\"appId\":72,\"clientSecret\":\"ccsvbyhhz09162@^\",\"secretName\":\"PBE加密/解密\",\"clientId\":\"T\\u0084êÄÆ\\u0010è÷\",\"appName\":\"加密测试\",\"principalName\":\"admin\",\"dateend\":1682092800000,\"params\":{},\"isInternal\":\"0\",\"approveRemark\":\"\",\"createBy\":\"\",\"createTime\":1681984499092,\"secretType\":\"0\",\"datefrom\":1680278400000,\"status\":\"0\"}";
        System.out.println("加密前数据：String:"+data);
        System.out.println("加密前数据：byte[]:"+showByteArray(URLDecoder.decode(data.toString(), "UTF-8").getBytes(StandardCharsets.UTF_8)));


        String encryptData=encrypt(data,clientSecret, clientId.getBytes(StandardCharsets.ISO_8859_1));

        byte[] encryptDataxxx = encryptData.getBytes(StandardCharsets.UTF_8);
        String decryptData=decrypt(new String(encryptDataxxx,StandardCharsets.UTF_8),clientSecret,clientId.getBytes(StandardCharsets.ISO_8859_1));

    }
}
